package com.lin.demo.aspacts;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.lin.demo.annotations.Resubmit;
import com.lin.demo.config.AppException;
import com.lin.demo.utils.RedisUtil;
import com.sun.istack.internal.NotNull;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.List;
import java.util.UUID;

/**
 * @Author zimingl
 * @Date 2023/4/2 10:58
 * @Description: ResubmitAspect
 */

@Aspect
@Component
public class ResubmitAspect {
    private final static String VALUE = UUID.randomUUID().toString();

    /**
     * aop 方法 处理重复请求逻辑
     * controller 入参的第一个参数为实体类型 一般用于添加接口
     *
     * @param joinPoint 切入点
     * @return Object
     * @throws Throwable Throwable
     */
    @Around(value = "@annotation(com.lin.demo.annotations.Resubmit)")
    public Object handleResubmit(ProceedingJoinPoint joinPoint) throws Throwable {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        // 获取注解信息
        Resubmit annotation = method.getAnnotation(Resubmit.class);
        int delaySeconds = annotation.delaySeconds();
        Object[] pointArgs = joinPoint.getArgs();
        String keyStr = "";
        // 获取第一个参数
        Object firstParam = pointArgs[0];
        JSONArray jsonArray = new JSONArray();
        if (firstParam instanceof List) {
            jsonArray.addAll(JSONObject.parseArray(JSON.toJSONString(firstParam)));
        } else {
            jsonArray.add(JSONObject.parseObject(JSON.toJSONString(firstParam)));
        }
        // 解析参数
        if (CollectionUtils.isNotEmpty(jsonArray)) {
            keyStr = getMD5String(jsonArray);
        }
        // 执行锁
        // 设置解锁key
        boolean lock = RedisUtil.lock(keyStr, VALUE, delaySeconds);
        if (lock) {
            // 放行
            return joinPoint.proceed();
        } else {
            // 响应重复提交异常
            throw new AppException(HttpStatus.BAD_REQUEST.value(), "请勿重复提交！");
        }
    }

    @NotNull
    private String getMD5String(JSONArray jsonArray) {
        String keyStr;
        StringBuffer stringBuffer = new StringBuffer();
        for (Object object : jsonArray) {
            JSONObject jsonObject = (JSONObject) object;
            jsonObject.forEach((key, value) -> {
                stringBuffer.append(value);
            });
        }
        // 生成加密参数 使用了content_MD5的加密方式
        keyStr = RedisUtil.handleKey(stringBuffer.toString());
        return keyStr;
    }
}
